Skip to content

webview 和原生交互是如何实现的,搭桥的原理

方式一 注入式

Android

Android: webView.addJavascriptInterface('', 'JSBridge') H5: window.JSBridge.postMessage()

js
webView.addJavascriptInterface(new WebAppInterface(this), "JSBridge");

iOS 安全

iOS: H5: window.webkit.messageHandlers.JSBridge.postMessage("Hello, iOS!");

evaluateJavaScript 在webview容器中执行js

方式二:iframe 拦截式

基于 iframe 修改 src 值,NA 可以捕获 url 变化,根据 url 变化,判断 H5 的意图 WebView url scheme 拦截。

  • FE 通过 scheme 拦截的方式向 native 主动发送消息
  • 例如:<iframe src="ihybrid://toast?data={parmas: \"test\"}&**callback**=**cb**common**32423**2"></iframe>
  • 如果 native 收到消息后需要将处理结果回传给 FE
  • 通过调用 FE 发送消息时参数中携带的回调函数
  • 这个回调函数挂载在 window 上面
  • 例如:window.cbcommon324232(res);
js

function createHybrid() {
  var protocol = !isLocal ? 'ihybrid://' : '/action/'
  var callbackCount = 0

  function createBridge() {
      var bridge = document.createElement('iframe')
      bridge.setAttribute('style', 'display:none;')
      bridge.setAttribute('height', '0px')
      bridge.setAttribute('width', '0px')
      bridge.setAttribute('frameborder', '0')
      document.documentElement.appendChild(bridge)
      return bridge
  }

  function buildUrl(action: any, params: any, callback: any) {
      var url = protocol + action + '?'
      var id = guid()
      url += 'data=' + encodeURIComponent(JSON.stringify(params || {}))
      if (callback) {
        var callbackName = '__cb__' + action + '__' + id + '__' + callbackCount++
        if (isLocal) {
            __cb__ = callbackName
        }
        win[callbackName] = function() {
          let args: any = arguments;
          args = [].slice.call(args)
          if(args.length === 0) args.push('');
          args.push(callbackName)
          callback && callback.apply(window, args)
          if (typeof (callback.remove) == 'function') {
            !!callbackName && callback.remove.apply(callback, [callbackName])
          } else {
            delete win[callbackName]
          }
        }
        url += '&__callback__=' + callbackName
      }
      return url
  }

  return function (action: any, params?: any, callback?: Function) {
      let bridge: any = createBridge()
      if (params && Object.prototype.toString.call(params) === '[object Function]') {
          callback = params
          params = {}
      }
      var url = buildUrl(action, params, callback);

      callback && isLocal && (bridge.onload = function(){
          var data = {};
          try {
              data = JSON.parse(bridge.contentDocument.body.children[0].textContent || "{}");
          } catch (e) {}  

          win[__cb__](data);
          __cb__ = null;
      });

      bridge.setAttribute('src', url);
      (function (bridge) {
          bridge.timer = setTimeout(() => {
            bridge.parentNode.removeChild(bridge)
            clearTimeout(bridge.timer)
          }, 3000)
      })(bridge);
  }
}

在 MIT 许可下发布